<?php
fily_XMLConverter::registerNamespace('control', fily_Control);
/**
 * Represents a basic html fily_Control.
 */
class fily_Control implements fily_ChildControl, fily_XMLToControl {

    /**
     * the html tag
     * @var string
     */
    protected $tag;

    /**
     * is set to true when the code is prepared
     * @var boolean
     */
    protected $prepared = false;

    /**
     * array of the attributes for this control
     * @var fily_KeyValueArray
     */
    public $attributes;


    /**
     * array of the style values
     * @var fily_KeyValueArray
     */
    public $style;

    /**
     * contains functions which are called on a specific javascipt event
     * @var array
     */
    protected $clientEvents = array();

    protected $endTagRequired = false;

    protected $lateBindingObjects = array();


    /**
     *
     * @param string $tag the html tag
     */
    public function __construct($tag='') {
        $this->attributes = new fily_KeyValueArray();
        $this->tag = $tag;
    }

    /**
     * parent from this control
     * @var fily_ControlToHtml
     */
    protected $parent;
    /**
     * returns the parent from this control
     * @return fily_ControlToHtml
     */
    public function getParent() {
        return $this->parent;
    }

    /**
     * set the parent from this control
     * @param fily_ControlToHtml $parent
     */
    public function setParent(fily_ControlToHtml $parent = null) {
        $this->parent = $parent;
    }


      /**
     * system id
     * @var string
     */
    protected $sid;
    /**
     * system id
     * @return string
     */
    public function getSid() {
        return $this->sid;
    }

    /**
     * system id
     * @param string $sid
     */
    public function setSid($sid) {
        $this->sid = $sid;
    }

    /**
     * converts this control to real html
     * @return <type>
     */
    public function toHtml() {
        // if fily_Html not prepared than prepare it
        if(!$this->prepared) {
            $this->prepare();
        }

        $this->cleanUp();
        $html = $this->generateHtml();
        $this->prepared = false;
        return $html;
    }

    protected function cleanUp() {
        // clean html first before creating it cause maybe we created some mess while preparing
        if($this->attributes->get('id')!=null&&!fily_ClientCodeManager::idIsUsed($this->attributes->get('id'))) {
            // delete this not required id
            $this->attributes->set('id', null);
        }
    }

    protected function generateHtml() {
        $html = '';
        if(!$this->getEndTagRequired()) {
            $html .= '<' . $this->getTag() . $this->attributes->toString() . ' />';
        }
        else {
            $html .= '<' . $this->getTag() . $this->attributes->toString() . ' ></'. $this->getTag() .'>';
        }
        return $html;
    }



    /**
     * prepares the control for the html output
     */
    public function prepare() {
        $this->prepared = true;

        if(count($this->clientEvents)>0) {
            $code = sprintf("var fily_init_temp_%s = document.getElementById('%s');", $this->getId(), $this->getId());
            fily_ClientCodeManager::addCode($code);
            fily_ClientCodeManager::useId($this->getId());
        }
        foreach ($this->clientEvents as $clientEvent) {
            $code = sprintf("firelynx.listenToEvent(fily_init_temp_%s,'%s', %s, %s); ", $this->getId(), $clientEvent['event'],$clientEvent['executionTime'],$clientEvent['function'] );
            fily_ClientCodeManager::addCode($code);
        }
    }




    /**
     * shortcut for toHtml()
     */
    public function __toString() {
        return $this->toHtml();
    }

    /**
     * return the boolean if a end tag is required
     * @return bool
     */
    public function getEndTagRequired() {
        return $this->endTagRequired;
    }

    /**
     * set  the boolean if a end tag is required
     * @param bool $endTagRequired
     */
    public function setEndTagRequired($endTagRequired) {
        $this->endTagRequired = $endTagRequired;
    }

    /**
     * returns the html tag
     * @return string
     */
    public function getTag() {
        return strtolower($this->tag);
    }

    /**
     * sets the html tag
     * @param string $tag
     */
    public function setTag($tag) {
        $this->tag = $tag;
    }


    /**
     * returns the html id
     * if the ids isn't set it gets a new id
     * @return string
     */
    public function getId() {
        if($this->attributes->get('id')==null) {
            $parentId = "";
            // get id from the parent
            if($this->getParent()!=null) {
                $parentId = $this->getParent()->getId();
            }
            // parentId is set
            if($parentId!='') {
                // add a seperator
                $parentId .= '_';
            }
            $id = self::createId($parentId);
            $this->setId($id);

        }

        return $this->attributes->get('id');
    }
    /**
     * registered ids
     * @var array
     */
    protected static $registeredIds = array();

    /**
     * register the id, required for unique id's
     * @param <type> $id
     */
    public static function registerId($id) {
        self::$registeredIds[] = $id;
    }

    /**
     * create a unique id
     * @param string $prefix
     * @return string
     */
    public static function createId($prefix) {
        $i = 0;
        // search a free id
        do {
            $i++;
            if($i < 10) {
                // starts with a zero
                $id = $prefix . 'ctl0' .$i;
            }
            else {
                $id = $prefix . 'ctl' .$i;
            }
        } while(self::idExists($id));

        self::registerId($id);
        return $id;
    }

    /**
     *  check if id exists
     * @param string $id
     * @return boolean
     */
    public static function idExists($id) {
        $exists = false;
        foreach(self::$registeredIds as $registeredId) {
            if($registeredId==$id) {
                $exists = true;
                break;
            }
        }
        return $exists;
    }

    /**
     * sets the html id
     * @param string $id
     */
    public function setId($id) {
        $this->attributes->set('id', $id);
        fily_ClientCodeManager::useId($id);
    }

    /**
     * get the style attribute
     * @return fily_KeyValueArray
     */
    public function getStyle() {
        if($this->attributes->get('style')==null) {
            $style = new fily_KeyValueArray();
            $style->setOutputFormat('{0}:{1};');
            $this->attributes->set('style', $style);
        }

        return  $this->attributes->get('style');
    }

    /**
     * sets the name of this control
     * @param string $value
     */
    public function setName($value) {
        $this->attributes->set('name', $value);
    }

    /**
     * returns the name
     * @return <type>
     */
    public function getName() {
        return $this->attributes->get('name');
    }

    /**
     * sets the css class
     * @param string $value
     */
    public function setClass($value) {
        $this->attributes->set('class', $value);
    }

    /**
     * returns the css class
     * @return <type>
     */
    public function getClass() {
        return $this->attributes->get('class');
    }

    /**
     * calls a javascript function on a event
     * @param string $event
     * @param string $function
     * @param int $executionZone
     */
    public function onClientEvent($event, $function, $executionTime = 500) {
        $this->clientEvents[] = array(
                'event' => $event,
                'function' => $function,
                'executionTime' => $executionTime,
        );
    }

    /**
     * finds a control with the specific id
     * @param string $id
     * @return fily_ControlToHtml
     */
    public function getControlById($id) {
        return null;
    }

    /**
     * finds a control with the specific system id
     * @param string $sid
     * @return fily_ControlToHtml
     */
    public function getControlBySid($sid) {
        return null;
    }


    public function XMLAddControl(fily_ControlToHtml $control) {
        throw new Exception('XML Convert : No inner control allowed for ' . get_class($this) );
    }

    public function XMLAttributes(array $attributes, $innerValue) {
        foreach($attributes as $key => $value) {
            switch ($key) {
                case 'style':
                // parse style
                    $value = trim($value);
                    $styles  = split(';', $value);
                    foreach($styles as $style) {
                        list($styleKey,$styleValue ) = split(':', $style);
                        $this->getStyle()->set($styleKey, $styleValue);
                    }
                    break;
                case 'id':
                    $this->setId($value);
                    break;
                case 'tag':
                    $this->setTag($value);
                    break;
               case 'sid':
                    $this->setSid($value);
                    break;
                default:
                    $this->attributes->set($key, $value);
                    break;
            }
        }
    }


    public function XMLCallFunction($name, array $parameters,$innerValue,fily_ControlToHtml $innerControl = null) {
        switch ($name) {
            case 'onclientevent':
                if(isset($parameters['executiontime']) ) {
                    $this->onClientEvent($parameters['event'], $parameters['function'], $parameters['executiontime']);
                }
                else {
                    $this->onClientEvent($parameters['event'], $parameters['function']);
                }
                break;
            default:
                throw new Exception('XML Convert : function "'. $name.'" not know for ' .  get_class($this));
                break;
        }
    }

    public function XMLLateBinding(fily_ControlToHtml $baseControl) {
        // reset
        $this->lateBindingObjects = array();
    }

    public function find($expression) {
        $control = null;
        if(!fily_ExpressionHelper::isExpression($expression)) {
            //expression is an id
            $control = $this->getControlById($expression);
        }
        else {
            $expressionParts = fily_ExpressionHelper::getParts($expression) ;
            // first expression part is always empty
            if(count($expressionParts)>1) {
                switch ($expressionParts[1]) {
                    case 'next':
                        $control =  fily_ExpressionHelper::next($expressionParts, $expression, $this);
                        break;
                    case 'previous':
                        $control = fily_ExpressionHelper::previous($expressionParts, $expression, $this);
                        break;
                    case 'parent':
                        $control = fily_ExpressionHelper::parent($expressionParts, $expression, $this);
                        break;
                    case 'root':
                        $control = fily_ExpressionHelper::root($expressionParts, $expression, $this);
                        break;
                }
            }
            // found a control and have another expressions
            if($control!=null && !empty($expression)) {
                $control = $control->find($expression);
            }
        }
        return $control;
    }



}
?>
